package com.bite.job.handler;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bit.common.redis.service.RedisService;
import com.bite.common.core.constants.CacheConstants;
import com.bite.common.core.constants.Constants;
import com.bite.common.core.constants.PatternConstants;
import com.bite.common.core.utils.ThreadLocalUtil;
import com.bite.job.domain.exam.Exam;
import com.bite.job.domain.message.Message;
import com.bite.job.domain.message.MessageText;
import com.bite.job.domain.message.vo.MessageTextVO;
import com.bite.job.domain.user.UserExam;
import com.bite.job.domain.user.UserQuestionScore;
import com.bite.job.manager.ExamCacheManager;
import com.bite.job.mapper.exam.ExamMapper;
import com.bite.job.mapper.user.UserExamMapper;
import com.bite.job.mapper.user.UserSubmitMapper;
import com.bite.job.service.IMessageService;
import com.bite.job.service.IMessageTextService;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Component
public class ExamXxlJobHandler {
    @Autowired
    private ExamCacheManager examCacheManager;

    @Autowired
    private RedisService redisService;

    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private UserSubmitMapper userSubmitMapper;

    @Autowired
    private UserExamMapper userExamMapper;

    @Autowired
    private IMessageTextService messageTextService;

    @Autowired
    private IMessageService messageService;

    @Value("${message.pattern.content}")
    private String contentPattern; // "您参与的竞赛：${examTitle}, 参与竞赛一共 ${totalUser} 人, 您排第 ${order} 名}"

    @Value("${message.pattern.title}")
    private String titlePattern; // "${examTitle} --- 排名情况"


    @XxlJob("examListHandler")
    public void examListHandler() {
        log.info("****  examListHandler  ****");
        List<Exam> examList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .select(Exam::getExamId, Exam::getTitle, Exam::getStartTime, Exam::getEndTime)
                .eq(Exam::getStatus, Constants.TRUE)
                .orderByDesc(Exam::getCreateTime));
        List<Exam> finishList = examList.stream().filter(exam -> isBeforeNow(exam.getEndTime())).toList();
        refreshCache(finishList, CacheConstants.EXAM_FINISHED_LIST);


        List<Exam> unfinishList = examList.stream().filter(exam -> !isBeforeNow(exam.getEndTime())).toList();
        refreshCache(unfinishList, CacheConstants.EXAM_UNFINISHED_LIST);
    }

    @XxlJob("userScoreHandler")
    public void userScoreHandler() {
        ThreadLocalUtil.setUserId(Constants.SYSTEM_ID);
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime minusDays = now.minusDays(1); // 获取前一天的值

        List<Exam> exams = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                .select(Exam::getExamId, Exam::getTitle)
                .gt(Exam::getEndTime, minusDays)
                .lt(Exam::getEndTime, now));
        Set<Long> examIdList = exams.stream().map(Exam::getExamId).collect(Collectors.toSet());
        List<UserQuestionScore> userQuestionScores = userSubmitMapper.selectUserScore(examIdList)
                .stream().filter(userQuestionScore -> userQuestionScore.getExamId() != null).toList();
        // 获取到合并的分数
        userQuestionScores = integrateUserScore(userQuestionScores, examIdList);
        Map<Long, List<UserQuestionScore>> examMap = userQuestionScores.stream().collect(Collectors.groupingBy(UserQuestionScore::getExamId));
        updateUserExamScore(examMap);
        insertMessage(exams, examMap);
    }

    // 更新参加竞赛的用户数据
    private void updateUserExamScore(Map<Long, List<UserQuestionScore>> examMap) {
        List<UserExam> updateList = new ArrayList<>();
        examMap.forEach((examId, userQuestionScoreList) -> {
            int rank = 1;
            for (UserQuestionScore userQuestionScore : userQuestionScoreList) {
                UserExam userExam = new UserExam();
                userExam.setExamId(examId);
                userExam.setUserId(userQuestionScore.getUserId());
                userExam.setScore(userQuestionScore.getScore());
                userExam.setUserRank(rank);
                updateList.add(userExam);
                rank++;
            }
        });
        userExamMapper.updateUserExamScore(updateList);
        // 删除缓存中的分数，让客户端重新刷新
        examCacheManager.deleteRankList(examMap.keySet());
    }

    private void insertMessage(List<Exam> exams, Map<Long, List<UserQuestionScore>> examMap) {
        if (CollectionUtil.isEmpty(exams)) return;
        List<MessageText> messageTextList = new ArrayList<>();
        List<Message> messageList = new ArrayList<>();
        Map<String, MessageTextVO> messageTextVOMap = new HashMap<>();

        for (Exam exam : exams) {
            String examContent = contentPattern.replaceAll(PatternConstants.MESSAGE_CONTENT_EXAM_TITLE, exam.getTitle());
            String examTitle = titlePattern.replaceAll(PatternConstants.MESSAGE_TITLE_EXAM_TITLE, exam.getTitle());
            List<UserQuestionScore> userQuestionScores = examMap.get(exam.getExamId());
            // 每个用户都要发送消息
            int rank = 1;
            int totalUser = userQuestionScores.size();
            for (UserQuestionScore userQuestionScore : userQuestionScores) {
                String content = examContent
                        .replaceAll(PatternConstants.MESSAGE_CONTENT_EXAM_TOTAL_USER, totalUser + "")
                        .replaceAll(PatternConstants.MESSAGE_CONTENT_EXAM_ORDER, rank + "");
                MessageText messageText = new MessageText();
                messageText.setMessageContent(content);
                messageText.setMessageTitle(examTitle);
                messageTextList.add(messageText);
                // 编写发送方与接收方
                Message message = new Message();
                message.setSendId(ThreadLocalUtil.getUserId());
                message.setReceiveId(userQuestionScore.getUserId());
                messageList.add(message);
                rank++;
            }
            // 添加
            messageTextService.batchInsert(messageTextList);
            // 插入完后再添加 因为原先是没有Id
            for (int i = 0; i < messageTextList.size(); i++) {
                MessageText messageText = messageTextList.get(i);
                Message message = messageList.get(i);
                message.setTextId(messageText.getTextId());
                MessageTextVO messageTextVO = new MessageTextVO();
                BeanUtil.copyProperties(messageText, messageTextVO);
                messageTextVOMap.put(getMessageDetailKey(messageText.getTextId()), messageTextVO);
            }
            messageService.batchInsert(messageList);

            Map<Long, List<Message>> userMessageMap = messageList.stream().collect(Collectors.groupingBy(Message::getReceiveId));
            Map<String, List<Long>> userMsgIdMap = new HashMap<>();
            userMessageMap.forEach((userId, messages) -> {
                String userMessageListKey = getUserMessageListKey(userId);
                List<Long> msgTextIdList = new ArrayList<>(messages.stream().map(Message::getTextId).toList());
                Collections.reverse(msgTextIdList);
//                userMsgIdMap.put(userMessageListKey, msgTextIdList);
//                redisService.rightPushAll(userMessageListKey, msgTextIdList);
                redisService.leftPushAll(userMessageListKey, msgTextIdList); // 因为要将最新的插在最上面
            });
            // Id 这个列表就不要用这样的方法 因为这种方法是用来存储String类型的
//            redisService.multiSet(userMsgIdMap);

            // 详情
            redisService.multiSet(messageTextVOMap);
        }
    }

    private List<UserQuestionScore> integrateUserScore(List<UserQuestionScore> userQuestionScores, Set<Long> examIdList) {
        Map<String, UserQuestionScore> map = userQuestionScores.stream().collect(Collectors.toMap(o -> o.getExamId() + "" + o.getUserId()
                , a -> a, (t, t2) -> {
                    t.setScore(t.getScore() + t2.getScore());
                    return t;
                }));
        List<UserQuestionScore> result = new ArrayList<>();
        map.forEach((key, val) -> {
            result.add(map.get(key));
        });
        result.sort((o1, o2) -> -(o1.getScore() - o2.getScore()));

        // 合并完后还需要将报名的但是没有答题的算进去
        List<UserExam> userExams = userExamMapper.selectList(new LambdaQueryWrapper<UserExam>()
                        .select(UserExam::getExamId, UserExam::getUserId)
                .in(UserExam::getExamId, examIdList));
        Set<Long> scoreUserIdSet = userQuestionScores.stream().map(UserQuestionScore::getUserId).collect(Collectors.toSet());
        for (UserExam userExam : userExams) {
            if (scoreUserIdSet.contains(userExam.getUserId())) continue;
            UserQuestionScore userQuestionScore = new UserQuestionScore();
            userQuestionScore.setUserId(userExam.getUserId());
            userQuestionScore.setExamId(userExam.getExamId());
            result.add(userQuestionScore);
        }

        return result;
    }


    private void refreshCache(List<Exam> examList, String cacheKey) {
        if (CollectionUtil.isEmpty(examList)) {
            return;
        }

        Map<String, Exam> examMap = new HashMap<>();
        List<Long> examIdList = new ArrayList<>();
        for (Exam exam : examList) {
            examMap.put(getDetailKey(exam.getExamId()), exam);
            examIdList.add(exam.getExamId());
        }
        redisService.multiSet(examMap);  //刷新详情缓存
        redisService.deleteObject(cacheKey);
        redisService.rightPushAll(cacheKey, examIdList);      //刷新列表缓存
    }

    private String getDetailKey(Long examId) {
        return CacheConstants.EXAM_DETAIL + examId;
    }

    private String getUserMessageListKey(Long userId) {
        return CacheConstants.USER_MESSAGE_LIST + userId;
    }

    private String getMessageDetailKey(Long messageTextId) {
        return CacheConstants.MESSAGE_DETAIL + messageTextId;
    }

    private boolean isBeforeNow(LocalDateTime dateTime) {
        return dateTime.isBefore(LocalDateTime.now());
    }

}
